﻿using System;
using OpenTK;
using OpenTK.Graphics.OpenGL;

namespace Jayden.Dll.OpenGL
{
	public class View : GameWindow
	{

		protected override void OnLoad(EventArgs e)
		{
			base.OnLoad(e);


			VSync = VSyncMode.On;
			//GL.Enable(EnableCap.ColorMaterial);
		}

		protected override void OnResize(EventArgs e)
		{
			base.OnResize(e);


			GL.Viewport(ClientRectangle);
			Matrix4d projection = Matrix4d.CreatePerspectiveFieldOfView(Math.PI / 4.0, ClientRectangle.Width / (double)ClientRectangle.Height, 1, 32);
			GL.MatrixMode(MatrixMode.Projection);
			GL.LoadMatrix(ref projection);
		}

		protected override void OnRenderFrame(FrameEventArgs e)
		{
			base.OnRenderFrame(e);
			Display();
		}

		public void EnableLight()
		{
			GL.Enable(EnableCap.Lighting);
			GL.Enable(EnableCap.Light0);
		}
		public void DisableLight()
		{
			GL.Disable(EnableCap.Lighting);
			GL.Disable(EnableCap.Light0);
		}
		public void DefineLights()
		{
			GL.Light(LightName.Light0, LightParameter.Position, new Vector4(1, 1, 0, 0));
		}
		public void DefineViewport()
		{
			GL.Viewport(ClientRectangle);
			GL.MatrixMode(MatrixMode.Projection);
			GL.LoadIdentity();
			PlaceViewport();
			DefineLights();
		}
		public void PlaceViewport()
		{
			Matrix4d projection = Matrix4d.CreatePerspectiveFieldOfView(Math.PI / 4.0, ClientRectangle.Width / (double)ClientRectangle.Height, 1, 32);
			GL.MultMatrix(ref projection);
			Matrix4 modelView = Matrix4.LookAt(new Vector3(10, 10, -8), new Vector3(0, 0, 0), Vector3.UnitY);
			GL.MatrixMode(MatrixMode.Modelview);
			GL.LoadMatrix(ref modelView);
		}
		const int SPHERE_NAME = 1;
		const int CUBE_NAME = 2;
		protected virtual void Render()
		{
			GL.PushName(SPHERE_NAME);
			if(Selected == SPHERE_NAME)
				GL.Color3(1.0f, 1.0f, 1.0f);
			else
				GL.Color3(1.0f, 0.0f, 0.0f);
			Helper.Sphere(1, 16, 16);
			GL.PopName();
			GL.Translate(1, 0, 0);
			GL.PushName(CUBE_NAME);
			if (Selected == CUBE_NAME)
				GL.Color3(1.0f, 1.0f, 1.0f);
			else
				GL.Color3(0.0f, 1.0f, 0.0f);
			Helper.Cube(1);
			GL.PopName();
		}

		public void Display()
		{
			GL.ClearColor(1.0f, 0.0f, 1.0f, 0.0f);
			GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
			EnableLight();
			DefineViewport();
			GL.PushMatrix();
			Render();
			GL.PopMatrix();

			SwapBuffers();
		}

		private void PickMatrix(double x, double y, double deltax, double deltay, int[] viewport)
		{
			if (deltax <= 0 || deltay <= 0)
			{
				return;
			}

			/* Translate and scale the picked region to the entire window */
			GL.Translate((viewport[2] - 2 * (x - viewport[0])) / deltax,
				(viewport[3] - 2 * (y - viewport[1])) / deltay, 0);
			GL.Scale(viewport[2] / deltax, viewport[3] / deltay, 1.0);
		}


		static int[] selectBuffer = new int[128 * 4];
		protected int Picking(int x, int y)
		{
			GL.SelectBuffer(128 * 4, selectBuffer);
			GL.InitNames();
			GL.RenderMode(RenderingMode.Select);
			GL.MatrixMode(MatrixMode.Projection);
			GL.LoadIdentity();
			int[] viewport = new int[4];
			GL.GetInteger(GetPName.Viewport, viewport);
			PickMatrix(x, ClientRectangle.Height - y, 5.0, 5.0, viewport);
			PlaceViewport();
			GL.Flush(); // ?
			GL.PushMatrix();
			Render();
			GL.PopMatrix();

			int found = GL.RenderMode(RenderingMode.Render);
			uint closest = uint.MaxValue;
			int selectedId = -1;

			for (int i = 0; i < found; i++)
			{
				uint distance = (uint)selectBuffer[i * 4 + 1];

				if (closest >= distance)
				{
					closest = distance;
					selectedId = (int)selectBuffer[i * 4 + 3];
				}
			}

			return selectedId;
		}

		int Selected = -1;

		protected override void OnUpdateFrame(FrameEventArgs e)
		{
			base.OnUpdateFrame(e);

			Selected = Picking(Mouse.X, Mouse.Y);
		}


	}
}
